package cn.sc.summer.security.config;

import cn.sc.summer.security.filter.AppLoginFilter;
import cn.sc.summer.security.filter.LoginFilter;
import cn.sc.summer.security.filter.SmsCodeLoginFilter;
import cn.sc.summer.security.handler.LogoutSuccessHandlerX;
import cn.sc.summer.token.constant.LoginAPIConstant;
import cn.sc.summer.token.handler.server.AccessDeniedHandlerX;
import cn.sc.summer.token.handler.server.AuthenticationEntryPointX;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityWebConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private LoginFilter loginFilter;

    @Resource
    private SmsCodeLoginFilter smsCodeLoginFilter;

    @Resource
    private AppLoginFilter appLoginFilter;

    @Resource
    private AuthorizationManagerX authorizationManagerX;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        log.info("==> Start initialization the Spring Security config...");

        //端点检测放行
        httpSecurity.authorizeHttpRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint())
                .permitAll()
                .anyRequest().access(authorizationManagerX);

        //关闭csrf防护
        httpSecurity.csrf()
                .disable();

        // 禁用httpBasic
        httpSecurity.httpBasic()
                .disable();

        //异常处理
        httpSecurity.exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPointX())
                .accessDeniedHandler(new AccessDeniedHandlerX());

        // 退出处理
        httpSecurity.logout()
                .logoutUrl(LoginAPIConstant.LOGOUT)
                .logoutSuccessHandler(new LogoutSuccessHandlerX());

        //表示替换掉原来的UsernamePasswordAuthenticationFilter的位置
        httpSecurity.addFilterAt(loginFilter, UsernamePasswordAuthenticationFilter.class);
        httpSecurity.addFilterAt(smsCodeLoginFilter, UsernamePasswordAuthenticationFilter.class);
        httpSecurity.addFilterAt(appLoginFilter, UsernamePasswordAuthenticationFilter.class);

    }

    @Bean
    public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return bean;
            }

            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                List<T> copy = mappings.stream()
                        .filter(mapping -> mapping.getPatternParser() == null)
                        .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }

            @SuppressWarnings("unchecked")
            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                try {
                    Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                    assert field != null;
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
            }
        };
    }

}

